#include <linux/tty.h>
#include <linux/kernel.h>

/* tty_queue 的大小：console 3*8; serial 3*2; pts 6*4  */
/* 最前面的3表示，每个控制台，分别对应读tty，写tty，和次要tty */
#define QUEUES	(3*(MAX_CONSOLES+NR_SERIALS+2*NR_PTYS))
/* tty队列：终端需要使用队列来读写以及转换数据，每个终端对配置3个tty队列，用于读写缓冲 */
static struct tty_queue tty_queues[QUEUES];
/* 终端实例：定义终端的结构以及如何使用 */
struct tty_struct tty_table[256];

/* 以下都是各个终端类型指向的tty队列起始地址 */
/* con_queues指向的是 console tty */
#define con_queues tty_queues
/* rs_queues指向的是serial tty */
#define rs_queues ((3*MAX_CONSOLES) + tty_queues)
/* mpty_queues 指向的是 pts(前4个) */
#define mpty_queues ((3*(MAX_CONSOLES+NR_SERIALS)) + tty_queues)
/* spty_queues 指向的是 pts0（后4个) */
#define spty_queues ((3*(MAX_CONSOLES+NR_SERIALS+NR_PTYS)) + tty_queues)

/* 以下都是各个终端的起始地址 */
#define con_table tty_table	        /* 控制台终端/虚拟终端 64 */
#define rs_table (64+tty_table)     /* 串口终端 64 */
#define mpty_table (128+tty_table)  /* 主伪终端 64 */
#define spty_table (192+tty_table)  /* 从伪终端 64 */

void chr_dev_init(void)
{
}

/**
 *  这里涉及几个概念
 * tty_queue --- 跟具体的输入输出实体关联起来的，能操作输入输出
 * tty_struct --- 使用 tty_queue 的实例，也就是终端
 *      更多的时候跟会话和登录关联起来了，更上层的会话概念
 *  */
void tty_init(void)
{
    int i;
    /* 初始化 tty_queue */
    for (i = 0; i < QUEUES; i++) {
        /* 数据头尾读写缓冲区 */
        tty_queues[i] = (struct tty_queue) {0,0,0,0,""};
    }
    /**
     * 串口 tty 初始化	
		rs_queues[0] --> tty_queues[24]  	read_q
		rs_queues[1] ---> tty_queues[25]	write_q
		rs_queues[3] ---> tty_queues[26]	read_q
		rs_queues[4] ---> tty_queues[27]	write_q
	*/
    /* 初始化终端结构 tty_struct */
    for (i = 0; i < 256; i++) {
        tty_table[i] =  (struct tty_struct) {
		 	{0, 0, 0, 0, 0, INIT_C_CC}, /* INIT_C_CC 是控制字符 */
			0, 0, 0, NULL, NULL, NULL, NULL
		};
    }
    con_init();

    /**
	 * 实际上，这里的 console 对应虚拟终端
	 * （另外还有一个console，默认是第一个虚拟终端，它主要是执行系统命令和打印内核信息，
     *      模拟主机上的本地终端而非远程）
	 * serial 对应串口终端（理论上是连接物理终端）
	 * pts 对应伪终端（用于远程连接，例如ssh等；或用于图像界面中的模拟终端窗口）
	 */

    /**
	 * NR_CONSOLES 在 console.c 中初始化
	 * 表示控制台对应的窗口数量，实际就是虚拟终端的个数
	 * 每个虚拟终端，都对应 读tty 写tty 和 次要tty 三个数据队列
	 */
    for (i = 0 ; i < NR_CONSOLES; i++) {
        con_table[i] = (struct tty_struct) {
		 	{ICRNL,			/* 将输入的回车转为换行 */
			OPOST|ONLCR,	/* 将输出的回车转为回车换行 */
			0,
			IXON | ISIG | ICANON | ECHO | ECHOCTL | ECHOKE,
			0,				/* console termio */
			INIT_C_CC}, 	/* 控制字符 */
			0,				/* 初始的 pgrp */
			0,				/* 初始的 session */
			0,				/* 初始的 stopped */
			con_write, 		/* 向虚拟终端写 */
			con_queues+0+i*3,con_queues+1+i*3,con_queues+2+i*3
		};
    }

	/* 初始化串口终端 */
	for (i = 0; i < NR_SERIALS; i++) {
		rs_table[i] = (struct tty_struct) {
			{0, 			/* 不进行转换 */
			0,				/* 不进行转换 */
			B2400 | CS8,
			0,
			0,
			INIT_C_CC},
			0,
			0,
			0,
			rs_write,		/* 向串口终端写 */
			rs_queues+0+i*3,rs_queues+1+i*3,rs_queues+2+i*3
		};
	}

	/* 初始化伪终端 */
	for (i = 0 ; i < NR_PTYS; i++) {
		mpty_table[i] = (struct tty_struct) {
			{0, 			/* 不进行转换 */
			0,				/* 不进行转换 */
			B9600 | CS8,
			0,
			0,
			INIT_C_CC},
			0,
			0,
			0,
			mpty_write, /* 向主伪终端写 */
			mpty_queues+0+i*3,mpty_queues+1+i*3,mpty_queues+2+i*3
		};
		spty_table[i] = (struct tty_struct) {
			{0, 			/* 不进行转换 */
			0,				/* 不进行转换 */
			B9600 | CS8,
			IXON | ISIG | ICANON,
			0,
			INIT_C_CC},
			0,
			0,
			0,
			spty_write, /* 向从伪终端写 */
			spty_queues+0+i*3,spty_queues+1+i*3,spty_queues+2+i*3
		};
	}

	rs_init();
	printk("%d virtual consoles\n\r", NR_CONSOLES);
	printk("%d pty's\n\r", NR_PTYS);
}